package com.hk.core.autoconfigure.web;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.hk.commons.aware.BeanSelfProcessor;
import com.hk.commons.converters.Jsr310Converters;
import com.hk.commons.converters.StringToBooleanConverter;
import com.hk.commons.converters.StringToCalendarConverter;
import com.hk.commons.converters.StringToDateConverter;
import com.hk.commons.jackson.EnhanceAnnotationIntrospector;
import com.hk.commons.util.AuditField;
import com.hk.commons.util.Constants;
import com.hk.commons.util.JsonUtils;
import com.hk.commons.util.SpringContextHolder;
import com.hk.commons.util.date.DateTimeUtils;
import com.hk.core.jdbc.deserializer.ConditionQueryModelDeserializer;
import com.hk.core.jdbc.query.ConditionQueryModel;
import com.hk.core.jdbc.serializer.ConditionQueryModelSerializer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.ClassUtils;

import java.util.List;
import java.util.Locale;


/**
 * @author Kevin
 * <p>
 * 公用web 配置，将webmvc 与webflux 共用的配置抽取出来
 */
@AutoConfiguration
public class WebContextConfiguration {

    public static void addFormatters(FormatterRegistry registry) {
        registry.addConverter(StringToBooleanConverter.INSTANCE);
        registry.addConverter(new StringToDateConverter());
        registry.addConverter(new StringToCalendarConverter());
        registry.addConverter(Jsr310Converters.StringToLocalDateTimeConverter.INSTANCE);
        registry.addConverter(Jsr310Converters.StringToLocalDateConverter.INSTANCE);
        registry.addConverter(Jsr310Converters.StringToLocalTimeConverter.INSTANCE);
        registry.addConverter(Jsr310Converters.StringToYearConverter.INSTANCE);
        registry.addConverter(Jsr310Converters.StringToYearMonthConverter.INSTANCE);
    }

    @Bean
    public SpringContextHolder springContextHolder() {
        return new SpringContextHolder();
    }

    /**
     * 解决spring aop 调用内部方法不生效的问题
     */
    @Bean
    public BeanSelfProcessor beanSelfProcessor() {
        return new BeanSelfProcessor();
    }

    @Bean
    @ConditionalOnMissingBean(value = {RefreshBeanPropertyController.class})
    public RefreshBeanPropertyController refreshBeanPropertyController() {
        return new RefreshBeanPropertyController();
    }

    /**
     * <pre>
     * spring 会自动 注入{@link ObjectMapper},但此 bean在序列化 与反序列化 json 时，不支持 JDK 8的 日期 API，
     * 所在这里配置 支持JDK 8 的日期 API 功能，以及其它功能
     * </pre>
     * <pre>
     *     在使用 spring cloud stream 接受消息时,也会使用 {@link org.springframework.cloud.stream.converter.ApplicationJsonMessageMarshallingConverter#objectMapper}
     *     进行序列化,此对象配置在 {@link org.springframework.cloud.stream.config.ContentTypeConfiguration#configurableCompositeMessageConverter(ObjectProvider, List)}
     * </pre>
     *
     * @see org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperConfiguration#jacksonObjectMapper(Jackson2ObjectMapperBuilder)
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> {
            var filterProvider = new SimpleFilterProvider();
            filterProvider.addFilter(JsonUtils.IGNORE_ENTITY_SERIALIZE_FIELD_FILTER_ID,
                    SimpleBeanPropertyFilter.serializeAllExcept(AuditField.AUDIT_FIELD_ARRAY.toArray(new String[0])));
            jacksonObjectMapperBuilder.modules(JsonUtils.getModuleList(true))
                    .filters(filterProvider)
                    .annotationIntrospector(new EnhanceAnnotationIntrospector())
                    .timeZone(Constants.TIME_ZONE_SHANGHAI)
                    .simpleDateFormat(DateTimeUtils.YYYY_MM_DD_HH_MM_SS)
                    .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                    .failOnUnknownProperties(false)//当设置为 true时，如果对象中没有指定属性，而前端传过来了不存在的的属性将抛出异常
                    .failOnEmptyBeans(false)
                    .locale(Locale.CHINA);
            if (ClassUtils.isPresent("com.hk.core.jdbc.query.ConditionQueryModel", null)) {
                JacksonJdbcSupportUtils.config(jacksonObjectMapperBuilder);
            }
        };
    }

    private static class JacksonJdbcSupportUtils {

        private static void config(Jackson2ObjectMapperBuilder builder) {
            builder.deserializerByType(ConditionQueryModel.class, new ConditionQueryModelDeserializer());
            builder.serializerByType(ConditionQueryModel.class, new ConditionQueryModelSerializer());
        }
    }

}
